{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Grids: Non-Uniform Grids"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Some data cannot be easily represented on a grid of uniformly spaced vertices. It is still possible to create a grid object to represent such a dataset. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "\n",
    "import astropy.units as u\n",
    "import numpy as np\n",
    "\n",
    "from plasmapy.plasma import grids"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "grid = grids.NonUniformCartesianGrid(\n",
    "    np.array([-1, -1, -1]) * u.cm, np.array([1, 1, 1]) * u.cm, num=(50, 50, 50)\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Currently, all non-uniform data is stored as an unordered 1D array of points. Therefore, although the dataset created above falls approximately on a Cartesian grid, its treatment is identical to a completely unordered set of points"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "grid.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Many of the properties defined for uniform grids are inaccessible for non-uniform grids. For example, it is not possible to pull out an axis. However, the following properties still apply"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f\"Grid points: {grid.grid.shape}\")\n",
    "print(f\"Units: {grid.units}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Properties can be added in the same way as on uniform grids."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Bx = np.random.rand(*grid.shape) * u.T\n",
    "grid.add_quantities(B_x=Bx)\n",
    "print(grid)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Methods"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Many of the methods defined for uniform grids also work for non-uniform grids, however there is usually a substantial performance penalty in the non-uniform case.\n",
    "\n",
    "For example, `grid.on_grid` behaves similarly. In this case, the boundaries of the grid are defined by the furthest point away from the origin in each direction."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pos = np.array([[0.1, -0.3, 0], [3, 0, 0]]) * u.cm\n",
    "print(grid.on_grid(pos))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The same definition is used to define the grid boundaries in `grid.vector_intersects` "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pt0 = np.array([3, 0, 0]) * u.cm\n",
    "pt1 = np.array([-3, 0, 0]) * u.cm\n",
    "pt2 = np.array([3, 10, 0]) * u.cm\n",
    "\n",
    "print(f\"Line from pt0 to pt1 intersects: {grid.vector_intersects(pt0, pt1)}\")\n",
    "print(f\"Line from pt0 to pt2 intersects: {grid.vector_intersects(pt0, pt2)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Interpolating Quantities"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nearest-neighbor interpolation also works identically. However, volume-weighted interpolation is not implemented for non-uniform grids."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pos = np.array([[0.1, -0.3, 0], [0.5, 0.25, 0.8]]) * u.cm\n",
    "print(f\"Pos shape: {pos.shape}\")\n",
    "print(f\"Position 1: {pos[0, :]}\")\n",
    "print(f\"Position 2: {pos[1, :]}\")\n",
    "\n",
    "Bx_vals = grid.nearest_neighbor_interpolator(pos, \"B_x\")\n",
    "print(f\"Bx at position 1: {Bx_vals[0]:.2f}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
